/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.dao.impl;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.dao.DaoException;
import cn.easyplatform.dao.EntityDao;
import cn.easyplatform.dao.Page;
import cn.easyplatform.dao.utils.DaoUtils;
import cn.easyplatform.dao.utils.SqlUtils;
import cn.easyplatform.entities.BaseEntity;
import cn.easyplatform.entities.EntityInfo;
import cn.easyplatform.entities.beans.ResourceBean;
import cn.easyplatform.entities.transform.TransformerFactory;
import cn.easyplatform.lang.Nums;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.transaction.jdbc.JdbcTransactions;
import cn.easyplatform.type.EntityType;
import cn.easyplatform.type.SubType;
import com.alibaba.druid.sql.PagerUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;

import javax.sql.DataSource;
import java.sql.*;
import java.util.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class DefaultEntityDaoImpl extends AbstractDao implements EntityDao {

    /**
     * @param ds
     */
    public DefaultEntityDaoImpl(DataSource ds) {
        super(ds);
    }

    @Override
    public List<EntityInfo> getAllEntity(String table) {
        Statement stmt = null;
        ResultSet rs = null;
        try {
            Connection conn = JdbcTransactions.getConnection(ds);
            stmt = conn.createStatement();
            rs = stmt
                    .executeQuery("select entityId,name,desp,type,subType,content,status,ext0,ext1 from "
                            + table);
            List<EntityInfo> entities = new ArrayList<EntityInfo>();
            while (rs.next()) {
                String id = rs.getString(1);
                if (!Strings.isBlank(id)) {
                    EntityInfo e = new EntityInfo();
                    e.setId(id);
                    e.setName(rs.getString(2));
                    e.setDescription(rs.getString(3));
                    e.setType(rs.getString(4));
                    e.setSubType(rs.getString(5));
                    e.setContent(rs.getString(6));
                    String status = rs.getString(7);
                    e.setStatus(Strings.isBlank(status) == true ? 'R' : status.charAt(0));
                    e.setExt0(rs.getString(8));
                    e.setExt1(rs.getString(9));
                    entities.add(e);
                }
            }
            return entities;
        } catch (SQLException ex) {
            throw new DaoException("dao.access.getAllEntity", ex);
        } finally {
            DaoUtils.closeQuietly(stmt, rs);
        }
    }

    @Override
    public List<EntityInfo> getUpdatableEntity(String table) {
        Statement stmt = null;
        ResultSet rs = null;
        try {
            Connection conn = JdbcTransactions.getConnection(ds);
            stmt = conn.createStatement();
            rs = stmt
                    .executeQuery("select entityId,name,desp,type,subType,content,status,ext0,ext1 from "
                            + table + " where status<>'R'");
            List<EntityInfo> entities = new ArrayList<EntityInfo>();
            while (rs.next()) {
                EntityInfo e = new EntityInfo();
                e.setId(rs.getString(1));
                e.setName(rs.getString(2));
                e.setDescription(rs.getString(3));
                e.setType(rs.getString(4));
                e.setSubType(rs.getString(5));
                e.setContent(rs.getString(6));
                e.setStatus(rs.getString(7).charAt(0));
                e.setExt0(rs.getString(8));
                e.setExt1(rs.getString(9));
                entities.add(e);
            }
            return entities;
        } catch (SQLException ex) {
            throw new DaoException("dao.access.getUpdatableEntity", ex);
        } finally {
            DaoUtils.closeQuietly(stmt, rs);
        }
    }

    @Override
    public <T extends BaseEntity> List<T> getEntities(String table, String type, String subType) {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            Connection conn = JdbcTransactions.getConnection(ds);
            StringBuilder sb = new StringBuilder();
            sb.append("select entityId,name,desp,content,ext0,ext1 from ").append(table).append(" where type=? AND status<>'D'");
            if (subType != null)
                sb.append(" AND subType=?");
            stmt = conn
                    .prepareStatement(sb.toString());
            stmt.setString(1, type);
            if (subType != null)
                stmt.setString(2, subType);
            rs = stmt.executeQuery();
            List<T> entities = new ArrayList<>();
            while (rs.next()) {
                EntityInfo e = new EntityInfo();
                e.setType(type);
                e.setSubType(subType);
                e.setId(rs.getString(1));
                e.setName(rs.getString(2));
                e.setDescription(rs.getString(3));
                e.setContent(rs.getString(4));
                e.setExt0(rs.getString(5));
                e.setExt1(rs.getString(6));
                entities.add((T) TransformerFactory.newInstance().transformFromXml(e));
            }
            return entities;
        } catch (SQLException ex) {
            throw new DaoException("dao.access.getAllEntity", ex);
        } finally {
            DaoUtils.closeQuietly(stmt, rs);
        }
    }

    @Override
    public List<String[]> getEntities(String sql, Page page, Object... params) {
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            Connection conn = JdbcTransactions.getConnection(ds);
            if (page != null) {
                String dbType = DaoUtils.getDatabaseType(ds.toString());
                if (page.isGetTotal()) {
                    pstmt = conn.prepareStatement(PagerUtils.count(sql, dbType));
                    SqlUtils.setParameter(pstmt, params);
                    rs = pstmt.executeQuery();
                    rs.next();
                    page.setTotalCount(Nums.toInt(rs.getObject(1), 0));
                    DaoUtils.closeQuietly(pstmt, rs);
                    if (page.getTotalCount() == 0)
                        return Collections.emptyList();
                }
                if (!Strings.isBlank(page.getOrderBy()))
                    sql = sql + " order by " + page.getOrderBy();
                sql = DaoUtils.getDialect(dbType).getPageSql(sql, page);
            }
            pstmt = conn.prepareStatement(sql);
            SqlUtils.setParameter(pstmt, params);
            rs = pstmt.executeQuery();
            List<String[]> entities = new ArrayList<>();
            ResultSetMetaData rsmd = rs.getMetaData();
            int size = rsmd.getColumnCount();
            while (rs.next()) {
                String[] rc = new String[size];
                for (int i = 0; i < size; i++)
                    rc[i] = rs.getString(i + 1);
                entities.add(rc);
            }
            return entities;
        } catch (SQLException ex) {
            throw new DaoException("dao.access.getAllEntity", ex);
        } finally {
            DaoUtils.closeQuietly(pstmt, rs);
        }
    }

    @Override
    public List<ResourceBean> getCaches(String table, boolean sessionScope) {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            Connection conn = JdbcTransactions.getConnection(ds);
            stmt = conn
                    .prepareStatement("select entityId,name,desp,content,status from "
                            + table + " where subType=?");
            stmt.setString(1, SubType.CACHE.getName());
            rs = stmt.executeQuery();
            List<ResourceBean> entities = new ArrayList<ResourceBean>();
            while (rs.next()) {
                EntityInfo e = new EntityInfo();
                e.setType(EntityType.RESOURCE.getName());
                e.setSubType(SubType.CACHE.getName());
                e.setId(rs.getString(1));
                e.setName(rs.getString(2));
                e.setDescription(rs.getString(3));
                e.setContent(rs.getString(4));
                e.setStatus(rs.getString(5).charAt(0));
                ResourceBean rb = TransformerFactory.newInstance().transformFromXml(e);
                String scope = rb.getProps().get("scope");
                if (sessionScope) {
                    if ("session".equals(scope))
                        entities.add(rb);
                } else if ("application".equals(scope))
                    entities.add(rb);
            }
            return entities;
        } catch (SQLException ex) {
            throw new DaoException("dao.access.getAllEntity", ex);
        } finally {
            DaoUtils.closeQuietly(stmt, rs);
        }
    }

    @Override
    public EntityInfo getEntity(String table, String entityId) {
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            Connection conn = JdbcTransactions.getConnection(ds);
            pstmt = conn
                    .prepareStatement("select name,desp,type,subType,content,ext0,ext1 from "
                            + table + " where entityId=?");
            pstmt.setString(1, entityId);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                EntityInfo e = new EntityInfo();
                e.setId(entityId);
                e.setName(rs.getString(1));
                e.setDescription(rs.getString(2));
                e.setType(rs.getString(3));
                e.setSubType(rs.getString(4));
                e.setContent(rs.getString(5));
                e.setExt0(rs.getString(6));
                e.setExt1(rs.getString(7));
                return e;
            }
            return null;
        } catch (SQLException ex) {
            throw new DaoException("dao.access.getEntity", ex, entityId);
        } finally {
            DaoUtils.closeQuietly(pstmt, rs);
        }
    }

    @Override
    public void updateEntity(String table) {
        PreparedStatement pstmt = null;
        try {
            Connection conn = JdbcTransactions.getConnection(ds);
            pstmt = conn.prepareStatement("update " + table
                    + " set status='R' where status='U' or status='C'");
            pstmt.executeUpdate();
            DaoUtils.closeQuietly(pstmt);
            pstmt = null;
        } catch (SQLException ex) {
            throw new DaoException("dao.access.deleteAndUpdateEntity", ex);
        } finally {
            DaoUtils.closeQuietly(pstmt);
        }
    }

    private final static String allModel = "select modelId,name,desp,type,content,status,createUser from ep_model_info where modelId<>? and type=? AND status<>'D'";

    private final static String model = "select modelId,name,desp,type,content,status,createUser from ep_model_info where modelId=?";

    @Override
    public List<EntityInfo> getModels() {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            Connection conn = JdbcTransactions.getConnection(ds);
            stmt = conn.prepareStatement(allModel);
            stmt.setString(1, ResourceBean.ENTITY_DATASOURCE_ID);
            stmt.setString(2, EntityType.PROJECT.getName());
            rs = stmt.executeQuery();
            List<EntityInfo> entities = new ArrayList<EntityInfo>();
            while (rs.next()) {
                EntityInfo e = new EntityInfo();
                e.setId(rs.getString(1));
                e.setName(rs.getString(2));
                e.setDescription(rs.getString(3));
                e.setType(rs.getString(4));
                e.setContent(rs.getString(5));
                e.setStatus(rs.getString(6).charAt(0));
                e.setCreateUser(rs.getString(7));
                entities.add(e);
            }
            return entities;
        } catch (SQLException ex) {
            throw new DaoException("dao.access.getAllEntity", ex);
        } finally {
            DaoUtils.closeQuietly(stmt, rs);
        }
    }

    @Override
    public List<EntityInfo> getModels(String projectId, String type) {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            Connection conn = JdbcTransactions.getConnection(ds);
            stmt = conn.prepareStatement("select modelId,name,desp,content,status from ep_model_info where type=? and createUser=? AND status<>'D'");
            stmt.setString(1, type);
            stmt.setString(2, projectId);
            rs = stmt.executeQuery();
            List<EntityInfo> entities = new ArrayList<EntityInfo>();
            while (rs.next()) {
                EntityInfo e = new EntityInfo();
                e.setId(rs.getString(1));
                e.setName(rs.getString(2));
                e.setType(type);
                e.setDescription(rs.getString(3));
                e.setContent(rs.getString(4));
                e.setStatus(rs.getString(5).charAt(0));
                entities.add(e);
            }
            return entities;
        } catch (SQLException ex) {
            throw new DaoException("dao.access.getAllEntity", ex);
        } finally {
            DaoUtils.closeQuietly(stmt, rs);
        }
    }


    @Override
    public EntityInfo getModel(String id) {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            Connection conn = JdbcTransactions.getConnection(ds);
            stmt = conn.prepareStatement(model);
            stmt.setString(1, id);
            rs = stmt.executeQuery();
            if (rs.next()) {
                EntityInfo e = new EntityInfo();
                e.setId(rs.getString(1));
                e.setName(rs.getString(2));
                e.setDescription(rs.getString(3));
                e.setType(rs.getString(4));
                e.setContent(rs.getString(5));
                e.setStatus(rs.getString(6).charAt(0));
                e.setCreateUser(rs.getString(7));
                return e;
            }
            return null;
        } catch (SQLException ex) {
            throw new DaoException("dao.access.getEntity", ex);
        } finally {
            DaoUtils.closeQuietly(stmt, rs);
        }
    }

    @Override
    public void updateModel(EntityInfo model) {
        PreparedStatement pstmt = null;
        try {
            Connection conn = JdbcTransactions.getConnection(ds);
            if (model.getStatus() == 'U') {
                pstmt = conn
                        .prepareStatement("update ep_model_info set name=?,desp=?,content=?,updateDate=?,updateUser=? where modelId=?");
                pstmt.setString(1, model.getName());
                pstmt.setString(2, model.getDescription());
                pstmt.setString(3, model.getContent());
                pstmt.setTimestamp(4, new Timestamp(System.currentTimeMillis()));
                pstmt.setString(5, model.getUpdateUser());
                pstmt.setString(6, model.getId());
                pstmt.executeUpdate();
            } else if (model.getStatus() == 'C') {
                pstmt = conn
                        .prepareStatement("INSERT INTO ep_model_info (modelId,name,desp,type,content,createDate,createUser,status) VALUES (?,?,?,?,?,?,?,?)");
                pstmt.setString(1, model.getId());
                pstmt.setString(2, model.getName());
                pstmt.setString(3, model.getDescription());
                pstmt.setString(4, model.getType());
                pstmt.setString(5, model.getContent());
                pstmt.setTimestamp(6, new Timestamp(System.currentTimeMillis()));
                pstmt.setString(7, model.getCreateUser());
                pstmt.setString(8, "C");
                pstmt.executeUpdate();
            } else if (model.getStatus() == 'D') {
                pstmt = conn
                        .prepareStatement("delete from ep_model_info where modelId=?");
                pstmt.setString(1, model.getId());
                pstmt.executeUpdate();
            }
        } catch (SQLException ex) {
            throw new DaoException("dao.access.deleteAndUpdateEntity", ex);
        } finally {
            DaoUtils.closeQuietly(pstmt);
        }
    }

    @Override
    public void updateEntity(String table, EntityInfo entity) {
        PreparedStatement pstmt = null;
        try {
            Connection conn = JdbcTransactions.getConnection(ds);
            if (entity.getStatus() == 'U') {
                pstmt = conn
                        .prepareStatement("update "
                                + table
                                + " set name=?,content=?,updateDate=?,updateUser=?,status='U' where entityId=?");
                pstmt.setString(1, entity.getName());
                pstmt.setString(2, entity.getContent());
                pstmt.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
                pstmt.setString(4, entity.getUpdateUser());
                pstmt.setString(5, entity.getId());
                pstmt.executeUpdate();
            } else if (entity.getStatus() == 'C') {
                pstmt = conn
                        .prepareStatement("insert into "
                                + table
                                + " (entityId,name,type,subType,content,createDate,createUser,status) values (?,?,?,?,?,?,?,?)");
                pstmt.setString(1, entity.getId());
                pstmt.setString(2, entity.getName());
                pstmt.setString(3, entity.getType());
                pstmt.setString(4, entity.getSubType());
                pstmt.setString(5, entity.getContent());
                pstmt.setTimestamp(6, new Timestamp(System.currentTimeMillis()));
                pstmt.setString(7, entity.getUpdateUser());
                pstmt.setString(8, "R");
                pstmt.executeUpdate();
            } else {
                pstmt = conn.prepareStatement("delete from " + table
                        + "where entityId=?");
                pstmt.setString(1, entity.getId());
                pstmt.executeUpdate();
            }
        } catch (SQLException ex) {
            throw new DaoException("dao.access.deleteAndUpdateEntity", ex);
        } finally {
            DaoUtils.closeQuietly(pstmt);
        }
    }

    @Override
    public void setModelStatus(String id, String status) {
        PreparedStatement pstmt = null;
        try {
            Connection conn = JdbcTransactions.getConnection(ds);
            pstmt = conn.prepareStatement("UPDATE ep_model_info SET status=? WHERE modelId=?");
            pstmt.setString(1, status);
            pstmt.setString(2, id);
            pstmt.executeUpdate();
        } catch (SQLException ex) {
            throw new DaoException("dao.access.deleteAndUpdateEntity", ex);
        } finally {
            DaoUtils.closeQuietly(pstmt);
        }
    }

    @Override
    public char[] generateId() {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        PreparedStatement pstmt = null;
        try {
            //创建项目
            conn = ds.getConnection();
            conn.setAutoCommit(false);
            stmt = conn.createStatement();
            try {
                rs = stmt.executeQuery("SELECT * FROM ep_pid_info");
            } catch (SQLException e) {//表不存在
                stmt.addBatch("CREATE TABLE `ep_pid_info`  (`s0` tinyint(4),`s1` tinyint(4),`s2` tinyint(4),`s3` tinyint(4))");
                stmt.addBatch("INSERT INTO `ep_pid_info` VALUES (65, 48, 48, 48)");
                stmt.executeBatch();
                rs = stmt.executeQuery("SELECT * FROM ep_pid_info");
            }
            char[] result = new char[4];
            if (rs.next()) {
                char[] data = new char[4];
                data[0] = (char) rs.getByte(1);
                data[1] = (char) rs.getByte(2);
                data[2] = (char) rs.getByte(3);
                data[3] = (char) rs.getByte(4);
                System.arraycopy(data, 0, result, 0, 4);
                generateId(data, data.length - 1);
                pstmt = conn.prepareStatement("UPDATE `ep_pid_info` SET s0=?,s1=?,s2=?,s3=?");
                pstmt.setInt(1, data[0]);
                pstmt.setInt(2, data[1]);
                pstmt.setInt(3, data[2]);
                pstmt.setInt(4, data[3]);
                pstmt.executeUpdate();
            }
            conn.commit();
            return result;
        } catch (SQLException ex) {
            try {
                conn.rollback();
            } catch (SQLException e) {
            }
            throw new DaoException("dao.access.getProjectId", ex, ex.getMessage());
        } finally {
            DaoUtils.closeQuietly(rs);
            DaoUtils.closeQuietly(stmt);
            DaoUtils.closeQuietly(pstmt);
            DaoUtils.closeQuietly(conn);
        }
    }

    /**
     * 自增长id
     *
     * @param data
     * @param len
     */
    private void generateId(char[] data, int len) {
        for (int i = len; i >= 0; i--) {
            if (data[i] == 57) {//9
                data[i] = 65;//A
                return;
            } else if (data[i] == 90) {//Z
                if (i > 0) {
                    data[i] = 48;//0
                    generateId(data, i - 1);
                    return;
                } else {
                    //id溢出
                    throw new EasyPlatformWithLabelKeyException("app.id.full");
                }
            } else {
                data[i] = (char) (data[i] + 1);
                return;
            }
        }
    }

    @Override
    public Object selectObject(String statement, Object... parameter) {
        Connection conn = null;
        ResultSet rs = null;
        PreparedStatement pstmt = null;
        try {
            conn = JdbcTransactions.getConnection(ds);
            pstmt = conn.prepareStatement(statement);
            for (int i = 1; i <= parameter.length; i++)
                pstmt.setObject(i, parameter[i - 1]);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                Object obj = rs.getObject(1);
                if (obj instanceof Clob)
                    return IOUtils.toString(((Clob) obj).getAsciiStream(), "utf-8");
                else if (obj instanceof Blob)
                    return IOUtils.toByteArray(((Blob) obj).getBinaryStream());
                return obj;
            } else
                return null;
        } catch (Exception ex) {
            log.error("selectObject", ex);
            throw new DaoException("dao.biz.select.one.error", ex, statement, parameter);
        } finally {
            DaoUtils.closeQuietly(rs);
            DaoUtils.closeQuietly(pstmt);
        }
    }

    @Override
    public Map<String, Object> selectOne(String statement, Object... parameter) {
        Connection conn = null;
        ResultSet rs = null;
        PreparedStatement pstmt = null;
        try {
            conn = JdbcTransactions.getConnection(ds);
            pstmt = conn.prepareStatement(statement);
            for (int i = 1; i <= parameter.length; i++)
                pstmt.setObject(i, parameter[i - 1]);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                ResultSetMetaData rsmd = rs.getMetaData();
                int size = rsmd.getColumnCount();
                Map<String, Object> data = new HashMap<>(size);
                for (int index = 1; index <= size; index++)
                    data.put(rsmd.getColumnName(index).toLowerCase(), rs.getObject(index));
                return data;
            } else
                return null;
        } catch (SQLException ex) {
            log.error("selectOne", ex);
            throw new DaoException("dao.biz.select.one.error", ex, statement, parameter);
        } finally {
            DaoUtils.closeQuietly(rs);
            DaoUtils.closeQuietly(pstmt);
        }
    }

    @Override
    public List<Map<String, Object>> selectList(String statement, Object... parameter) {
        Connection conn = null;
        ResultSet rs = null;
        PreparedStatement pstmt = null;
        try {
            conn = JdbcTransactions.getConnection(ds);
            pstmt = conn.prepareStatement(statement);
            for (int i = 1; i <= parameter.length; i++)
                pstmt.setObject(i, parameter[i - 1]);
            rs = pstmt.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            int size = rsmd.getColumnCount();
            List<Map<String, Object>> data = new ArrayList<>();
            while (rs.next()) {
                Map<String, Object> record = new HashMap<>(size);
                for (int index = 1; index <= size; index++)
                    record.put(rsmd.getColumnName(index).toLowerCase(), rs.getObject(index));
                data.add(record);
            }
            return data;
        } catch (SQLException ex) {
            log.error("selectList", ex);
            throw new DaoException("dao.biz.query.error", ex, statement, parameter);
        } finally {
            DaoUtils.closeQuietly(rs);
            DaoUtils.closeQuietly(pstmt);
        }
    }

    @Override
    public int update(String statement, Object... parameter) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = JdbcTransactions.getConnection(ds);
            pstmt = conn.prepareStatement(statement);
            SqlUtils.setParameter(pstmt, parameter);
            return pstmt.executeUpdate();
        } catch (SQLException ex) {
            log.error("update", ex);
            if (statement.length() > 20)
                statement = statement.substring(0, 20) + "...";
            if (parameter.length > 5)
                parameter = ArrayUtils.subarray(parameter, 0, 5);
            throw new DaoException("dao.biz.update.sql.error", ex, statement, parameter);
        } finally {
            DaoUtils.closeQuietly(pstmt);
        }
    }

    @Override
    public long insert(String statement, Object... parameter) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = JdbcTransactions.getConnection(ds);
            pstmt = conn.prepareStatement(statement, Statement.RETURN_GENERATED_KEYS);
            SqlUtils.setParameter(pstmt, parameter);
            pstmt.executeUpdate();
            rs = pstmt.getGeneratedKeys();
            if (rs.next())
                return rs.getLong(1);
            return 0;
        } catch (SQLException ex) {
            log.error("update", ex);
            if (statement.length() > 20)
                statement = statement.substring(0, 20) + "...";
            if (parameter.length > 5)
                parameter = ArrayUtils.subarray(parameter, 0, 5);
            throw new DaoException("dao.biz.update.sql.error", ex, statement, parameter);
        } finally {
            DaoUtils.closeQuietly(pstmt, rs);
        }
    }
}
